package org.leeloo.internal.core;

import org.eclipse.debug.core.DebugException;
import org.eclipse.jdt.debug.core.IJavaDebugTarget;
import org.eclipse.jdt.debug.core.IJavaObject;
import org.eclipse.jdt.debug.core.IJavaStackFrame;
import org.eclipse.jdt.debug.core.IJavaThread;
import org.eclipse.jdt.debug.core.IJavaValue;

/**
 * Utility for using {@link IJavaObject java objects}.
 * 
 * @author Brad Reynolds
 */
public class JavaObjects {
    public static ObjectDescriptor getObjectDescriptor(IJavaObject javaObject, IJavaStackFrame stackFrame) {
        ObjectDescriptor descriptor = new ObjectDescriptor();

        try {
            IJavaValue hashCode = javaObject.sendMessage("hashCode", "()I", null, (IJavaThread) stackFrame.getThread(), null); //$NON-NLS-1$//$NON-NLS-2$
            String className = javaObject.getJavaType().getName();
            descriptor.setClassName(className);
            descriptor.setIdentityHashCode(Integer.parseInt(hashCode.getValueString()));
        } catch (DebugException e) {
            throw new RuntimeException(e);
        }
        return descriptor;
    }

    /**
     * @param javaObject
     * @param stackFrame
     * @param className
     * @return <code>true</code> if the <code>javaObject</code> is an
     *         instance of <code>className</code>.
     */
    public static boolean isInstance(IJavaObject javaObject, IJavaStackFrame stackFrame, String className) {
        if (javaObject == null) {
            throw new IllegalArgumentException("Parameter javaObject was null."); //$NON-NLS-1$
        }
        if (stackFrame == null) {
            throw new IllegalArgumentException("Parameter stackFrame was null."); //$NON-NLS-1$
        }
        if (className == null) {
            throw new IllegalArgumentException("Parameter className was null."); //$NON-NLS-1$
        }

        try {
            IJavaThread thread = (IJavaThread) stackFrame.getThread();
            IJavaDebugTarget target = (IJavaDebugTarget) stackFrame.getDebugTarget();

            if (javaObject.equals(target.nullValue())) {
                return false;
            }

            IJavaObject objectClass = (IJavaObject) javaObject.sendMessage("getClass", //$NON-NLS-1$
                    "()Ljava/lang/Class;", //$NON-NLS-1$
                    null,
                    thread,
                    false);

            // get the classloader
            IJavaObject objectClassLoader = (IJavaObject) objectClass.sendMessage("getClassLoader", //$NON-NLS-1$
                    "()Ljava/lang/ClassLoader;", //$NON-NLS-1$
                    null,
                    thread,
                    false);

            IJavaValue clazz = target.newValue(className);
            IJavaObject loadedClass = (IJavaObject) objectClassLoader.sendMessage("loadClass", //$NON-NLS-1$
                    "(Ljava/lang/String;)Ljava/lang/Class;", //$NON-NLS-1$
                    new IJavaValue[] { clazz },
                    thread,
                    false);

            if (loadedClass.equals(target.nullValue())) {
                return false;
            }

            IJavaValue isControl = loadedClass.sendMessage("isInstance", "(Ljava/lang/Object;)Z", new IJavaValue[] { javaObject }, thread, false); //$NON-NLS-1$ //$NON-NLS-2$
            IJavaValue trueValue = target.newValue(true);

            return isControl.equals(trueValue);
        } catch (DebugException e) {
            e.printStackTrace();
        }

        return false;
    }
}
